package com.nhncorp.neptune.changelog;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

import junit.framework.TestCase;

import com.nhncorp.neptune.common.Constants;
import com.nhncorp.neptune.common.LockServiceFactory;
import com.nhncorp.neptune.common.conf.NConfiguration;
import com.nhncorp.neptune.common.exception.ChangeLogException;
import com.nhncorp.neptune.common.util.NetworkUtil;
import com.nhncorp.pleiades.client.LockService;
import com.nhncorp.pleiades.client.LockService.LockMode;
import com.nhncorp.pleiades.protocol.LockMetaData;

public class TestServerLocationManager extends TestCase {
  NConfiguration conf;
  LockService lockService;
  ServerLocationManager manager;

  public TestServerLocationManager() {
    conf = new NConfiguration();
    conf.set("testmode", true);
    conf.set("neptune.filesystem", "local");
    conf.set("neptune.changelog.filesystem", "pipe");
    conf.set("neptune.local.temp", System.getProperty("user.home") + File.separator + ".neptune_local");

    try {
      lockService = LockServiceFactory.getLockService(conf, "localhost", true);
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    }
  }


  protected void setUp() throws Exception {
    try {
      Thread.sleep(1000);
    } catch(Exception e) {
    }
    System.out.println("==== set up ====");
    lockService.deleteNode(Constants.CHANGELOG_SERVER, true);
    try {
      Thread.sleep(1000);
    } catch(Exception e) {
    }
  }
  
  protected void tearDown() throws Exception {

    System.out.println("==== tear down ====");
  }

  public void testGetAddresses() {
    ArrayList<String> addrList = new ArrayList<String>();

    addrList.add("10.8.115.126");
    addrList.add("10.8.115.127");
    addrList.add("10.8.115.128");

    for (String addr : addrList) {
      createNode(addr + ":8000");
    }

    try {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
      }

      manager = ServerLocationManager.instance(conf);

      assertTrue(3 <= manager.getAllAddresses().length);

      InetSocketAddress[] list = manager.listManager.electChangeLogServers();

      for (String addr : addrList) {
        int i = 0;
        for(; i < list.length; i++) {
          if (list[i].getAddress().getHostName().equals(addr)) {
            break;
          }
          
          assertTrue(i != list.length);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    } finally {
      for (String addr : addrList) {
        deleteNode(addr + ":8000");
      }

      manager.clear();
    }
  }

  public void testGetAddressesWithLocal() {
    ArrayList<String> addrList = new ArrayList<String>();

    try {
      addrList.add(InetAddress.getLocalHost().getHostAddress());
    } catch (UnknownHostException e1) {
      e1.printStackTrace();
      assertTrue(false);
    }
    addrList.add("10.8.115.127");
    addrList.add("10.8.115.124");

    for (String addr : addrList) {
      createNode(addr + ":8000");
    }

    try {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
      }

      manager = ServerLocationManager.instance(conf);
     
      assertEquals(3, manager.getAllAddresses().length);

      InetSocketAddress[] list = manager.listManager.electChangeLogServers();

      assertEquals(InetAddress.getLocalHost(), list[0].getAddress());
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    } finally {
      for (String addr : addrList) {
        deleteNode(addr + ":8000");
      }
      manager.clear();
    }
  }
  
  public void testInsufficientChangeLogServers() {
    try {
      manager = ServerLocationManager.instance(conf);
      assertTrue(false);
    } catch (ChangeLogException e) {
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    }
    
    ArrayList<String> addrList = new ArrayList<String>();

    addrList.add("10.8.115.127");
    addrList.add("10.8.115.124");

    for (String addr : addrList) {
      createNode(addr + ":8000");
    }
    
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
    
    try {
      manager = ServerLocationManager.instance(conf);
      assertTrue(false);
    } catch (ChangeLogException e) {
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    } finally {
      for (String addr : addrList) {
        deleteNode(addr + ":8000");
      }
    }
  }

  public void testNodeAdded() {

    ArrayList<String> addrList = new ArrayList<String>();

    addrList.add("10.8.115.123");
    addrList.add("10.8.115.127");
    addrList.add("10.8.115.124");

    for (String addr : addrList) {
      createNode(addr + ":8000");
    }
    
    try {
      Thread.sleep(1000);
      manager = ServerLocationManager.instance(conf);
      assertEquals(addrList.size(), manager.getAllAddresses().length);
      
      try {
        createNode(InetAddress.getLocalHost().getHostAddress() + ":8000");
        addrList.add(InetAddress.getLocalHost().getHostAddress());
      } catch (UnknownHostException e1) {
        e1.printStackTrace();
        assertTrue(false);
      }
      
      Thread.sleep(1000);
      
      assertEquals(addrList.size(), manager.getAllAddresses().length);
      assertEquals(InetAddress.getLocalHost()
          , manager.listManager.electChangeLogServers()[0].getAddress());
    } catch (InterruptedException e) {
      e.printStackTrace();
      assertTrue(false);
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    } finally {
      for (String addr : addrList) {
        deleteNode(addr + ":8000");
      }
    }
  }

  private void createNode(String hostName) {
    String lockPath = Constants.CHANGELOG_SERVER + "/" + NetworkUtil.hostNameToFileName(hostName);
    try {
      if (lockService.tryLock(lockPath, LockMode.LOCK_EXCLUSIVE)) {
        LockMetaData lockMetaData = new LockMetaData(lockPath);
        lockMetaData.setLockMode(LockService.LockMode.LOCK_EXCLUSIVE.ordinal());
        try {
          if (lockService.exist(lockPath)) {
            lockService.deleteNode(lockPath);
          }
        } catch (IOException e) {
          e.printStackTrace();
          assertTrue(false);
        }
        
        if (lockService.createNode(lockMetaData, true) == false) {
          System.err.println("error in creating node");
          assertTrue(false);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    }
  }

  private void deleteNode(String hostName) {
    String lockPath = Constants.CHANGELOG_SERVER + "/" + NetworkUtil.hostNameToFileName(hostName);
    try {
      if (lockService.tryLock(lockPath, LockMode.LOCK_EXCLUSIVE)) {
        LockMetaData lockMetaData = new LockMetaData(lockPath);
        lockMetaData.setLockMode(LockService.LockMode.LOCK_EXCLUSIVE.ordinal());

        if (lockService.deleteNode(lockPath) == false) {
          System.err.println("error in deleting node");
          assertTrue(false);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    }
  }
}
